/* * Copyright (C) 2011 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.errai.enterprise.rebind; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ws.rs.CookieParam; import javax.ws.rs.FormParam; import javax.ws.rs.HeaderParam; import javax.ws.rs.MatrixParam; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.PathSegment; import org.jboss.errai.codegen.Cast; import org.jboss.errai.codegen.Context; import org.jboss.errai.codegen.DefParameters; import org.jboss.errai.codegen.Parameter; import org.jboss.errai.codegen.Statement; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaClassFactory; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.meta.MetaParameter; import org.jboss.errai.codegen.util.Stmt; import org.jboss.errai.enterprise.client.jaxrs.api.MultivaluedMapImpl; import org.jboss.errai.enterprise.client.jaxrs.api.PathSegmentImpl; /** * Represents parameters of a JAX-RS resource method. * * @author Christian Sadilek <csadilek@redhat.com> */ public class JaxrsResourceMethodParameters { // path param examples which are matched by this regex: /{isbn}/aaa{param}bbb/{name}-{zip}/aaa{param:b+}/{many:.*} // leading and trailing white spaces are tolerated // see http://docs.jboss.org/resteasy/docs/2.3.4.Final/userguide/html_single/index.html#_PathParam private static final Pattern PATH_PARAM_PATTERN = Pattern.compile("(\\{\\s*)(\\w[\\w.-]*)(:\\s*([^{}][^{}]*))*(\\s*\\})"); private Statement entityParameter; private Map<Class<? extends Annotation>, MultivaluedMap<String, Statement>> parameters; public static JaxrsResourceMethodParameters fromMethod(MetaMethod method) { final List<Parameter> defParams = DefParameters.from(method).getParameters(); return fromMethod(method, defParams); } public static JaxrsResourceMethodParameters fromMethod(MetaMethod method, String parameterArrayVarName) { final List<Statement> params = new ArrayList<Statement>(); final Parameter[] defParms = DefParameters.from(method).getParameters().toArray(new Parameter[0]); for (int i = 0; i < defParms.length; i++) { final MetaClass type = defParms[i].getType().asBoxed(); final Statement s = Cast.to(type, Stmt.loadVariable(parameterArrayVarName, i)); params.add(new Statement() { @Override public String generate(Context context) { return s.generate(context); } @Override public MetaClass getType() { return type; } }); } return fromMethod(method, params); } public static JaxrsResourceMethodParameters fromMethod(MetaMethod method, List<? extends Statement> parameterValues) { final JaxrsResourceMethodParameters params = new JaxrsResourceMethodParameters(); int i = 0; for (final MetaParameter param : method.getParameters()) { final Statement paramValue = parameterValues.get(i++); Annotation a = param.getAnnotation(PathParam.class); if (a != null) { params.add(PathParam.class, ((PathParam) a).value(), paramValue); } else if ((a = param.getAnnotation(QueryParam.class)) != null) { params.add(QueryParam.class, ((QueryParam) a).value(), paramValue); } else if ((a = param.getAnnotation(HeaderParam.class)) != null) { params.add(HeaderParam.class, ((HeaderParam) a).value(), paramValue); } else if ((a = param.getAnnotation(MatrixParam.class)) != null) { params.add(MatrixParam.class, ((MatrixParam) a).value(), paramValue); } else if ((a = param.getAnnotation(FormParam.class)) != null) { params.add(FormParam.class, ((FormParam) a).value(), paramValue); } else if ((a = param.getAnnotation(CookieParam.class)) != null) { params.add(CookieParam.class, ((CookieParam) a).value(), paramValue); } else { params.setEntityParameter(paramValue, method); } } return params; } private void add(Class<? extends Annotation> type, String name, Statement value) { if (parameters == null) parameters = new HashMap<Class<? extends Annotation>, MultivaluedMap<String, Statement>>(); MultivaluedMap<String, Statement> params = parameters.get(type); if (params == null) { parameters.put(type, params = new MultivaluedMapImpl<String, Statement>()); } params.add(name, value); } public MultivaluedMap<String, Statement> getPathParameters() { return parameters.get(PathParam.class); } public Statement getPathParameter(String name) { final Statement param = getParameterByName(PathParam.class, name); if (param == null) throw new RuntimeException("No @PathParam found with name:" + name); if (MetaClassFactory.get(PathSegment.class).equals(param.getType())) { return new Statement() { @Override public String generate(Context context) { if (param instanceof Parameter) { return Stmt.castTo(PathSegmentImpl.class, Stmt.loadVariable(((Parameter) param).getName())) .invoke("getEncodedPathWithParameters").generate(context); } else { return Stmt.castTo(PathSegmentImpl.class, param).invoke("getEncodedPathWithParameters").generate(context); } } @Override public MetaClass getType() { return MetaClassFactory.get(String.class); } }; } return param; } public MultivaluedMap<String, Statement> getQueryParameters() { return get(QueryParam.class); } public List<Statement> getQueryParameters(String name) { return getQueryParameters().get(name); } public MultivaluedMap<String, Statement> getHeaderParameters() { return get(HeaderParam.class); } public List<Statement> getHeaderParameters(String name) { return getHeaderParameters().get(name); } public MultivaluedMap<String, Statement> getMatrixParameters() { return get(MatrixParam.class); } public Statement getMatrixParameter(String name) { return getParameterByName(MatrixParam.class, name); } public MultivaluedMap<String, Statement> getFormParameters() { return get(FormParam.class); } public MultivaluedMap<String, Statement> getCookieParameters() { return get(CookieParam.class); } public Statement getCookieParameter(String name) { return getParameterByName(CookieParam.class, name); } public Statement getEntityParameter() { return entityParameter; } private void setEntityParameter(Statement entityParameter, MetaMethod method) { if (this.entityParameter != null) { throw new RuntimeException("Only one non-annotated entity parameter allowed per method:" + method.getName()); } this.entityParameter = entityParameter; } private MultivaluedMap<String, Statement> get(Class<? extends Annotation> type) { if (parameters == null) return null; return parameters.get(type); } private Statement getParameterByName(Class<? extends Annotation> type, String name) { Statement param = null; if (get(type) != null) { final List<Statement> params = get(type).get(name); if (params != null && !params.isEmpty()) { param = params.get(0); } } return param; } public static List<String> getPathParameterExpressions(String path) { final String pathWithNestedBracesRemoved = replaceNestedCurlyBraces(path); final List<String> pathParamNames = new ArrayList<String>(); final Matcher matcher = PATH_PARAM_PATTERN.matcher(pathWithNestedBracesRemoved); while (matcher.find()) { final String id = matcher.group(2); final String regex = matcher.group(3); if (id != null) { final String pathParamExpr = recoverNestedCurlyBraces(id + ((regex != null) ? regex : "")); pathParamNames.add(pathParamExpr); } } return pathParamNames; } public boolean needsEncoding(String paramName) { // PathSegments are encoded in PathSegmentImpl if ((MetaClassFactory.get(PathSegment.class).equals(getParameterByName(PathParam.class, paramName).getType()))) { return false; } return true; } private static final char openCurlyReplacement = 6; private static final char closeCurlyReplacement = 7; private static String replaceNestedCurlyBraces(String str) { final char[] chars = str.toCharArray(); int open = 0; for (int i = 0; i < chars.length; i++) { if (chars[i] == '{') { if (open != 0) chars[i] = openCurlyReplacement; open++; } else if (chars[i] == '}') { open--; if (open != 0) { chars[i] = closeCurlyReplacement; } } } return new String(chars); } private static String recoverNestedCurlyBraces(String str) { return str.replace(openCurlyReplacement, '{').replace(closeCurlyReplacement, '}'); } }